﻿/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../global/head.h"
#include "../macro/head.h"
#include "odbc_fields.h"
#include "odbc_desc.h"
#include "odbc_stmt.h"
#include "odbc_dbc.h"

namespace xos_odbc
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    static odbc_dbc::POOL * pool_ptr = 0;

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    odbc_dbc::odbc_dbc()
    {
        init_data();
    }

    odbc_dbc::~odbc_dbc()
    {
        term();
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // public method

    int odbc_dbc::get_addr( POOL *** pppPool, LIST *** pppList )
    {
        int ret = 0;
        if( pppPool )
        {
            *pppPool = &pool_ptr;
        }
        if( pppList )
        {
            *pppList = 0;
        }
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // i_odbc_node method

    // 
    // 得到最近的错误信息
    // 
    int odbc_dbc::get_last_err_info( char * lpszInfo, int nSize, int * lpnInfoLen, int * lpnLenNeeded )
    {
        int ret = 0;

        if( 0 == ret )
        {
            m_err.get_info( lpszInfo, nSize, lpnInfoLen, lpnLenNeeded );
        }

        return ret;
    }

    // 
    // 利用不同数据库的功能生成对应sql放在lpszSql中，得到最后新增记录的自增ID。
    // 成功返回0,结果在lpIntId中
    // 需要在设置异步前执行
    // 
    int odbc_dbc::get_last_insert_id( const char * lpszSql, int * lpIntId )
    {
        int ret = 0;

        xos::xos_int StrLen_or_IndPtr = sizeof( xos::xos_i32 );
        xos_odbc::i_stmt * pStmt = 0;
        xos::xos_i32 nRetId = -1;

        if( 0 == ret )
        {
            ret = execute_direct( lpszSql, &pStmt );
        }

        if( 0 == ret )
        {
            int nColNum = 0;
            ret = pStmt->get_result_col_num( &nColNum );
            if( ( 0 != ret ) || ( 1 != nColNum ) )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            ret = pStmt->bind_colum( 0, xos_odbc::i_stmt::VT_I32, &nRetId, sizeof( nRetId ), &StrLen_or_IndPtr );
        }

        if( 0 == ret )
        {
            int r = pStmt->fetch();
            if( 0 == r )
            {
                if( lpIntId )
                {
                    *lpIntId = nRetId;
                }
            }
            else
            {
                ret = 1;
            }
        }

        if( pStmt )
        {
            pStmt->release();
            pStmt = 0;
        }

        return ret;
    }


    // 
    // 直接运行sql
    // 
    int odbc_dbc::execute_direct( const char * lpszSql, i_stmt ** ppStmt )
    {
        int ret = 0;

        SQLHANDLE hStmt = SQL_NULL_HSTMT;
        odbc_stmt * pStmt = 0;

        if( 0 == ret )
        {
            ret = create_stmt( ( i_stmt ** )&pStmt );
            if( 0 == ret )
            {
                hStmt = pStmt->handle();
            }
        }

        if( 0 == ret )
        {
            SQLRETURN r = SQLExecDirectA( hStmt, ( SQLCHAR * )lpszSql, SQL_NTS );
            ret = m_err.stmt_ret( hStmt, r );
        }

        if( ( 0 == ret ) && ppStmt )
        {
            *ppStmt = pStmt;
            pStmt = 0;
        }

        xos_stl::release_interface( pStmt );

        return ret;
    }

    // 
    // 编译sql
    // 
    int odbc_dbc::prepare_sql( const char * lpszSql, i_stmt ** ppStmt )
    {
        int ret = 0;

        SQLHANDLE hStmt = SQL_NULL_HSTMT;
        odbc_stmt * pStmt = 0;

        if( 0 == ret )
        {
            ret = create_stmt( ( i_stmt ** )&pStmt );
            if( 0 == ret )
            {
                hStmt = pStmt->handle();
            }
        }

        if( 0 == ret )
        {
            ret = pStmt->prepare_sql( lpszSql );
        }

        if( ( 0 == ret ) && ppStmt )
        {
            *ppStmt = pStmt;
            pStmt = 0;
        }

        xos_stl::release_interface( pStmt );

        return ret;
    }

    // 
    // 生成i_fields
    // 
    int odbc_dbc::create_fields( i_fields ** ppv )
    {
        int ret = 0;

        if( 0 == ret )
        {
            odbc_fields::T * pF = odbc_fields::get_item_from_pool( true );
            pF->set_connect( this );
            pF->init();
            *ppv = pF;
        }

        return ret;
    }

    // 
    // 创建一个htmt
    // 
    int odbc_dbc::create_stmt( i_stmt ** ppv )
    {
        int ret = 0;

        SQLHANDLE hStmt = SQL_NULL_HSTMT;

        if( ( 0 == ret ) && ( SQL_NULL_HDBC == m_hDbc ) )
        {
            ret = 1;
        }

        if( 0 == ret )
        {
            SQLRETURN r = SQLAllocHandle( SQL_HANDLE_STMT, m_hDbc, &hStmt );
            ret = m_err.stmt_ret( hStmt, r );
        }

        if( 0 == ret )
        {
            SQLRETURN r = SQLSetStmtAttr( hStmt, SQL_ATTR_CURSOR_TYPE, ( SQLPOINTER )SQL_CURSOR_STATIC, 0 );
            ret = m_err.stmt_ret( hStmt, r );
        }

        if( ( 0 == ret ) && ppv )
        {
            odbc_stmt::T * p = odbc_stmt::get_item_from_pool( true );
            p->set_handle( hStmt );
            if( m_bAsync )
            {
                p->set_async( true );
            }
            p->init();
            *ppv = p;
            hStmt = SQL_NULL_HSTMT;
        }

        if( SQL_NULL_HSTMT != hStmt )
        {
            SQLFreeHandle( SQL_HANDLE_STMT, hStmt );
            hStmt = SQL_NULL_HSTMT;
        }

        return ret;
    }

    int odbc_dbc::async_test( bool * bAsyncCon, bool * bAsyncStmt )
    {
        int ret = 0;

        SQLUINTEGER sqlRet = 0;

        SQLGetInfo( m_hDbc, SQL_ASYNC_MODE, ( SQLPOINTER )&sqlRet, sizeof( sqlRet ), NULL );

        if( bAsyncStmt && ( sqlRet & SQL_AM_STATEMENT ) )
        {
            *bAsyncStmt = true;
        }

        if( bAsyncCon && ( sqlRet & SQL_AM_CONNECTION ) )
        {
            *bAsyncCon = true;
        }

        return ret;
    }

    int odbc_dbc::set_connect_fun_async( bool bAsync )
    {
        int ret = 0;

        SQLRETURN sqlRet = SQL_SUCCESS;

        if( bAsync )
        {
            sqlRet = SQLSetConnectAttr( m_hDbc, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, ( SQLPOINTER )SQL_ASYNC_DBC_ENABLE_ON, SQL_IS_INTEGER );
        }
        else
        {
            sqlRet = SQLSetConnectAttr( m_hDbc, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, ( SQLPOINTER )SQL_ASYNC_DBC_ENABLE_OFF, SQL_IS_INTEGER );
        }

        {
            ret = m_err.connect_ret( m_hDbc, sqlRet );
            if( 0 != ret )
            {
                LOG4( "set connect function async info = %s", m_err.get_info( 0, 0, 0, 0 ) );
            }
        }

        return ret;
    }

    // 
    // 设置是否异步执行sql
    // 
    int odbc_dbc::set_async( bool bAsync )
    {
        int ret = 0;

        SQLRETURN sqlRet = SQL_SUCCESS;

        if( bAsync )
        {
            sqlRet = SQLSetConnectAttrA( m_hDbc, SQL_ATTR_ASYNC_ENABLE, ( SQLPOINTER )SQL_ASYNC_ENABLE_ON, SQL_IS_INTEGER );
        }
        else
        {
            sqlRet = SQLSetConnectAttrA( m_hDbc, SQL_ATTR_ASYNC_ENABLE, ( SQLPOINTER )SQL_ASYNC_ENABLE_OFF, SQL_IS_INTEGER );
        }

        {
            ret = m_err.connect_ret( m_hDbc, sqlRet );
            LOG4( "set connect async = %d, info = %s", bAsync, m_err.get_info( 0, 0, 0, 0 ) );
            m_bAsync = bAsync;
        }

        return ret;
    }

    // 
    //  0 : success. 
    //  1 : running. 
    // -1 : failed
    // 
    int odbc_dbc::end_trans( bool bSuccess )
    {
        int ret = 0;

        if( 0 == ret )
        {
            SQLRETURN r = SQLEndTran( SQL_HANDLE_DBC, m_hDbc, bSuccess ? SQL_COMMIT : SQL_ROLLBACK );
            ret = m_err.connect_ret( m_hDbc, r );
        }

        // 如果返回成功或失败，非pending状态，都是结束，改回直接提交模式
        if( 1 != ret )
        {
            SQLRETURN r = SQLSetConnectAttrA( m_hDbc, SQL_ATTR_AUTOCOMMIT, ( SQLPOINTER )SQL_AUTOCOMMIT_ON, 0 );
            m_err.connect_ret( m_hDbc, r );
        }

        return ret;
    }

    // 
    //  0 : success. 
    //  1 : running. 
    // -1 : failed
    // 
    int odbc_dbc::begin_trans()
    {
        int ret = 0;

        if( 0 == ret )
        {
            SQLRETURN r = SQLSetConnectAttrA( m_hDbc, SQL_ATTR_AUTOCOMMIT, ( SQLPOINTER )SQL_AUTOCOMMIT_OFF, 0 );
            ret = m_err.connect_ret( m_hDbc, r );
        }

        return ret;
    }

    // 
    //  0 : success. 
    //  1 : running. 
    // -1 : failed
    // 
    int odbc_dbc::connect()
    {
        int ret = 0;

        if( 0 == ret )
        {
            SQLCHAR * lpszOut = ( SQLCHAR * )m_szOutputString;
            SQLCHAR * lpszStr = (SQLCHAR*)m_con_str.c_str();
            m_out_len = sizeof( m_szOutputString ) - 1;
            int len = m_con_str.length();
            m_real_len = 0;
            SQLRETURN r = SQLDriverConnectA( m_hDbc, m_hWnd, lpszStr, len, lpszOut, m_out_len, &m_real_len, SQL_DRIVER_COMPLETE_REQUIRED );
            ret = m_err.connect_ret( m_hDbc, r );
        }

        if( 0 == ret )
        {
            ret = set_async( m_bAsync );
        }

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int odbc_dbc::set_connect_info( void * hWnd, const char * lpszConStr, bool bAsync )
    {
        int ret = 0;
        m_hWnd = ( SQLHWND )hWnd;
        m_con_str = lpszConStr;
        m_bAsync = bAsync;
        return ret;
    }

    int odbc_dbc::set_handle( SQLHANDLE hDbc )
    {
        int ret = 0;
        m_hDbc = hDbc;
        return ret;
    }

    int odbc_dbc::init()
    {
        int ret = 0;
        return ret;
    }

    int odbc_dbc::term()
    {
        int ret = 0;

        if( SQL_NULL_HDBC != m_hDbc )
        {
            SQLDisconnect( m_hDbc );
            SQLFreeHandle( SQL_HANDLE_DBC, m_hDbc );
            m_hDbc = SQL_NULL_HDBC;
        }

        init_data();

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int odbc_dbc::init_data()
    {
        int ret = 0;
        m_hDbc = SQL_NULL_HDBC;
        m_err.clear();
        m_con_str = "";
        m_bAsync = false;
        m_hWnd = 0;
        return ret;
    }

} // xos_odbc
